Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Commit 4f6bccea18587564dcae184f60ac1a07985b13a8


Parents : 50e3d60
Author : Ivan <e46112d44649266d71fe2193e00a4710>
Signature : T66BB85Valid, signed by author
Date : 2026-07-21T16:20:02-05:00

feat: update outbound message status handling with method-aware icons and titles, update localization for delivery statuses

Changes
Diff

diff --git a/meshchatx.rsm b/meshchatx.rsm
index 0417f775..8e4a39fd 100644
Binary files a/meshchatx.rsm and b/meshchatx.rsm differ

diff --git a/meshchatx/src/frontend/components/messages/ConversationMessageEntry.vue b/meshchatx/src/frontend/components/messages/ConversationMessageEntry.vue
index 49bad81d..3e50f257 100644
--- a/meshchatx/src/frontend/components/messages/ConversationMessageEntry.vue
+++ b/meshchatx/src/frontend/components/messages/ConversationMessageEntry.vue
@@ -309,17 +309,17 @@
</button>
<MaterialDesignIcon
v-if="entry.items[0].lxmf_message.state === 'delivered'"
- icon-name="check-all"
+ :icon-name="cv.outboundBubbleStatusIconName(entry.items[0].lxmf_message)"
class="size-3"
:class="cv.outboundBubbleDeliveredIconClass(entry.items[0])"
- title="Delivered"
+ :title="cv.outboundBubbleStatusTitle(entry.items[0].lxmf_message)"
/>
<MaterialDesignIcon
v-else-if="['sent', 'propagated', 'unknown'].includes(entry.items[0].lxmf_message.state)"
- icon-name="check"
+ :icon-name="cv.outboundBubbleStatusIconName(entry.items[0].lxmf_message)"
class="size-3"
:class="cv.outboundBubbleSentCheckIconClass(entry.items[0])"
- :title="cv.outboundSentStatusTitle(entry.items[0].lxmf_message)"
+ :title="cv.outboundBubbleStatusTitle(entry.items[0].lxmf_message)"
/>
<svg
v-if="cv.showRichOutboundPendingUi(entry.items[0]) && cv.isOutboundPendingForUi(entry.items[0])"
@@ -545,13 +545,15 @@
<template v-if="chatItem.is_outbound">
<MaterialDesignIcon
v-if="chatItem.lxmf_message.state === 'delivered'"
- icon-name="check-all"
+ :icon-name="cv.outboundBubbleStatusIconName(chatItem.lxmf_message)"
class="size-3 opacity-50"
+ :title="cv.outboundBubbleStatusTitle(chatItem.lxmf_message)"
/>
<MaterialDesignIcon
v-else-if="['sent', 'propagated', 'unknown'].includes(chatItem.lxmf_message.state)"
- icon-name="check"
+ :icon-name="cv.outboundBubbleStatusIconName(chatItem.lxmf_message)"
class="size-3 opacity-50"
+ :title="cv.outboundBubbleStatusTitle(chatItem.lxmf_message)"
/>
<span
v-else-if="['failed', 'cancelled', 'rejected'].includes(chatItem.lxmf_message.state)"
@@ -1240,21 +1242,21 @@
<MaterialDesignIcon icon-name="refresh" class="size-3 text-white" />
</button>
- <!-- delivered: double check -->
+ <!-- delivered / sent: method-aware status icon -->
<MaterialDesignIcon
v-if="chatItem.lxmf_message.state === 'delivered'"
- icon-name="check-all"
+ :icon-name="cv.outboundBubbleStatusIconName(chatItem.lxmf_message)"
class="size-3"
:class="cv.outboundBubbleDeliveredIconClass(chatItem)"
- title="Delivered"
+ :title="cv.outboundBubbleStatusTitle(chatItem.lxmf_message)"
/>
- <!-- sent: single check (include unknown for initial outbound when server confirmed creation) -->
+ <!-- sent: include unknown for initial outbound when server confirmed creation -->
<MaterialDesignIcon
v-else-if="['sent', 'propagated', 'unknown'].includes(chatItem.lxmf_message.state)"
- icon-name="check"
+ :icon-name="cv.outboundBubbleStatusIconName(chatItem.lxmf_message)"
class="size-3"
:class="cv.outboundBubbleSentCheckIconClass(chatItem)"
- :title="cv.outboundSentStatusTitle(chatItem.lxmf_message)"
+ :title="cv.outboundBubbleStatusTitle(chatItem.lxmf_message)"
/>
<svg
v-if="cv.showRichOutboundPendingUi(chatItem) && cv.isOutboundPendingForUi(chatItem)"

diff --git a/meshchatx/src/frontend/components/messages/ConversationViewer.vue b/meshchatx/src/frontend/components/messages/ConversationViewer.vue
index 7623da60..4a423618 100644
--- a/meshchatx/src/frontend/components/messages/ConversationViewer.vue
+++ b/meshchatx/src/frontend/components/messages/ConversationViewer.vue
@@ -4968,9 +4968,44 @@ export default {
return this.$t("messages.outbound_pending");
},
outboundSentStatusTitle(lxmfMessage) {
+ return this.outboundBubbleStatusTitle(lxmfMessage);
+ },
+ outboundBubbleStatusIconName(lxmfMessage) {
+ if (!lxmfMessage) {
+ return "check";
+ }
+ const state = lxmfMessage.state;
+ const method = lxmfMessage.method;
+ if (state === "delivered") {
+ if (method === "propagated") {
+ return "email-check-outline";
+ }
+ if (method === "paper") {
+ return "note-check-outline";
+ }
+ return "check-all";
+ }
+ if (["sent", "propagated", "unknown"].includes(state)) {
+ if (method === "propagated") {
+ return "email-outline";
+ }
+ if (method === "paper") {
+ return "note-outline";
+ }
+ return "check";
+ }
+ return "check";
+ },
+ outboundBubbleStatusTitle(lxmfMessage) {
if (!lxmfMessage) {
return "";
}
+ if (lxmfMessage.state === "delivered") {
+ if (lxmfMessage.method === "propagated") {
+ return this.$t("messages.outbound_delivered_propagated");
+ }
+ return this.$t("messages.outbound_delivered");
+ }
if (lxmfMessage.method === "propagated") {
return this.$t("messages.outbound_on_propagation_node");
}

diff --git a/meshchatx/src/frontend/locales/de.json b/meshchatx/src/frontend/locales/de.json
index d9a35cb6..4c4dfb09 100644
--- a/meshchatx/src/frontend/locales/de.json
+++ b/meshchatx/src/frontend/locales/de.json
@@ -1669,6 +1669,8 @@
"send_pathfinding_tooltip": "Pfad zum Peer wird gesucht (Reticulum ermittelt eine Route). Ihre Nachricht wird gesendet, sobald die Route bereit ist. Fahren Sie für Details mit der Maus über die Statussymbole in der Sprechblase.",
"outbound_on_propagation_node": "Auf dem Propagationsknoten",
"outbound_sent_network": "Gesendet",
+ "outbound_delivered": "Zugestellt",
+ "outbound_delivered_propagated": "Über Propagation zugestellt",
"more_actions": "Weitere Aktionen",
"retry_failed": "Fehlgeschlagene erneut senden",
"telemetry_history": "Telemetrie-Verlauf",

diff --git a/meshchatx/src/frontend/locales/en.json b/meshchatx/src/frontend/locales/en.json
index 2b09f237..1ce79cb5 100644
--- a/meshchatx/src/frontend/locales/en.json
+++ b/meshchatx/src/frontend/locales/en.json
@@ -1637,6 +1637,8 @@
"send_pathfinding_tooltip": "Finding path to peer (Reticulum is resolving a route). Your message sends as soon as the route is ready. Hover status icons in the bubble for details.",
"outbound_on_propagation_node": "On propagation node",
"outbound_sent_network": "Sent",
+ "outbound_delivered": "Delivered",
+ "outbound_delivered_propagated": "Delivered via propagation",
"no_messages_in_conversation": "No messages in this conversation yet.",
"say_hello": "Say hello!",
"no_active_chat": "No Active Chat",

diff --git a/meshchatx/src/frontend/locales/es.json b/meshchatx/src/frontend/locales/es.json
index 8d25a799..1e239638 100644
--- a/meshchatx/src/frontend/locales/es.json
+++ b/meshchatx/src/frontend/locales/es.json
@@ -1637,6 +1637,8 @@
"send_pathfinding_tooltip": "Buscando ruta al par (Reticulum está resolviendo una ruta). Su mensaje se enviará en cuanto la ruta esté lista. Pase el cursor sobre los iconos de estado en la burbuja para más detalles.",
"outbound_on_propagation_node": "En el nodo de propagación",
"outbound_sent_network": "Enviado",
+ "outbound_delivered": "Entregado",
+ "outbound_delivered_propagated": "Entregado vía propagación",
"no_messages_in_conversation": "Aún no hay mensajes en esta conversación.",
"say_hello": "¡Saluda!",
"no_active_chat": "Sin chat activo",

diff --git a/meshchatx/src/frontend/locales/fi.json b/meshchatx/src/frontend/locales/fi.json
index 920138a5..f699e517 100644
--- a/meshchatx/src/frontend/locales/fi.json
+++ b/meshchatx/src/frontend/locales/fi.json
@@ -1637,6 +1637,8 @@
"send_pathfinding_tooltip": "Etsitään polkua vertaisen luo (Reticulum etsii reittiä). Viestisi lähetetään heti kun polku on valmis. Kohdista kuplan tilakuvakkeisiin lisätietoja varten.",
"outbound_on_propagation_node": "Levityssolmussa",
"outbound_sent_network": "Lähetetty",
+ "outbound_delivered": "Toimitettu",
+ "outbound_delivered_propagated": "Toimitettu levityksen kautta",
"no_messages_in_conversation": "Tässä keskustelussa ei ole viestejä.",
"say_hello": "Sano hei!",
"no_active_chat": "Ei valittua keskustelua",

diff --git a/meshchatx/src/frontend/locales/fr.json b/meshchatx/src/frontend/locales/fr.json
index c2b7a371..f7a99827 100644
--- a/meshchatx/src/frontend/locales/fr.json
+++ b/meshchatx/src/frontend/locales/fr.json
@@ -1637,6 +1637,8 @@
"send_pathfinding_tooltip": "Recherche d'itinéraire vers le pair (Reticulum résout une route). Votre message sera envoyé dès que la route sera prête. Survolez les icônes d'état dans la bulle pour plus de détails.",
"outbound_on_propagation_node": "Sur le nœud de propagation",
"outbound_sent_network": "Envoyé",
+ "outbound_delivered": "Distribué",
+ "outbound_delivered_propagated": "Distribué via propagation",
"no_messages_in_conversation": "Pas encore de messages dans cette conversation.",
"say_hello": "Dis bonjour !",
"no_active_chat": "Pas de chat actif",

diff --git a/meshchatx/src/frontend/locales/it.json b/meshchatx/src/frontend/locales/it.json
index 71241d3c..3e876fa1 100644
--- a/meshchatx/src/frontend/locales/it.json
+++ b/meshchatx/src/frontend/locales/it.json
@@ -1669,6 +1669,8 @@
"send_pathfinding_tooltip": "Ricerca percorso verso il peer (Reticulum sta risolvendo un percorso). Il messaggio verrà inviato non appena il percorso sarà pronto. Passa il mouse sulle icone di stato nella bolla per i dettagli.",
"outbound_on_propagation_node": "Sul nodo di propagazione",
"outbound_sent_network": "Inviato",
+ "outbound_delivered": "Consegnato",
+ "outbound_delivered_propagated": "Consegnato via propagazione",
"more_actions": "Altre azioni",
"retry_failed": "Riprova messaggi non inviati",
"telemetry_history": "Cronologia telemetria",

diff --git a/meshchatx/src/frontend/locales/nl.json b/meshchatx/src/frontend/locales/nl.json
index 4b222b7f..0bba82b7 100644
--- a/meshchatx/src/frontend/locales/nl.json
+++ b/meshchatx/src/frontend/locales/nl.json
@@ -1637,6 +1637,8 @@
"send_pathfinding_tooltip": "Pad naar peer zoeken (Reticulum zoekt een route). Uw bericht wordt verzonden zodra de route klaar is. Beweeg de muis over de statuspictogrammen in de bubbel voor details.",
"outbound_on_propagation_node": "Op propagatieknooppunt",
"outbound_sent_network": "Verzonden",
+ "outbound_delivered": "Afgeleverd",
+ "outbound_delivered_propagated": "Afgeleverd via propagatie",
"no_messages_in_conversation": "Nog geen berichten in dit gesprek.",
"say_hello": "Zeg hallo!",
"no_active_chat": "Geen actief gesprek",

diff --git a/meshchatx/src/frontend/locales/ru.json b/meshchatx/src/frontend/locales/ru.json
index 54ac9c33..04b9e791 100644
--- a/meshchatx/src/frontend/locales/ru.json
+++ b/meshchatx/src/frontend/locales/ru.json
@@ -1669,6 +1669,8 @@
"send_pathfinding_tooltip": "Поиск пути к узлу (Reticulum определяет маршрут). Сообщение будет отправлено, как только маршрут будет готов. Наведите курсор на значки статуса в пузыре для подробностей.",
"outbound_on_propagation_node": "На узле распространения",
"outbound_sent_network": "Отправлено",
+ "outbound_delivered": "Доставлено",
+ "outbound_delivered_propagated": "Доставлено через распространение",
"more_actions": "Другие действия",
"retry_failed": "Повторить неудачные",
"telemetry_history": "История телеметрии",

diff --git a/meshchatx/src/frontend/locales/zh.json b/meshchatx/src/frontend/locales/zh.json
index 94a7cb21..cdf16bdf 100644
--- a/meshchatx/src/frontend/locales/zh.json
+++ b/meshchatx/src/frontend/locales/zh.json
@@ -1637,6 +1637,8 @@
"send_pathfinding_tooltip": "正在查找到节点的路径(Reticulum 正在解析路由)。路由就绪后消息将立即发送。将鼠标悬停在气泡中的状态图标上可查看详情。",
"outbound_on_propagation_node": "已在传播节点",
"outbound_sent_network": "已发送",
+ "outbound_delivered": "已送达",
+ "outbound_delivered_propagated": "已通过传播送达",
"no_messages_in_conversation": "对话中尚无消息",
"say_hello": "打个招呼!",
"no_active_chat": "无活动聊天",

diff --git a/tests/frontend/ConversationViewer.outboundPropagationStatus.test.js b/tests/frontend/ConversationViewer.outboundPropagationStatus.test.js
index 1f98fae0..76c7a914 100644
--- a/tests/frontend/ConversationViewer.outboundPropagationStatus.test.js
+++ b/tests/frontend/ConversationViewer.outboundPropagationStatus.test.js
@@ -79,6 +79,43 @@ describe("ConversationViewer outbound propagation status", () => {
expect(wrapper.vm.outboundSentStatusTitle(null)).toBe("");
});
+ it("outboundBubbleStatusTitle uses delivered and method-aware copy", () => {
+ const wrapper = mountViewer();
+ expect(wrapper.vm.outboundBubbleStatusTitle({ method: "direct", state: "delivered" })).toBe(
+ "messages.outbound_delivered"
+ );
+ expect(wrapper.vm.outboundBubbleStatusTitle({ method: "propagated", state: "delivered" })).toBe(
+ "messages.outbound_delivered_propagated"
+ );
+ expect(wrapper.vm.outboundBubbleStatusTitle({ method: "propagated", state: "sent" })).toBe(
+ "messages.outbound_on_propagation_node"
+ );
+ expect(wrapper.vm.outboundBubbleStatusTitle({ method: "paper", state: "sent" })).toBe(
+ "messages.outbound_sent_network"
+ );
+ expect(wrapper.vm.outboundBubbleStatusTitle(null)).toBe("");
+ });
+
+ it("outboundBubbleStatusIconName maps method and state to icons", () => {
+ const wrapper = mountViewer();
+ expect(wrapper.vm.outboundBubbleStatusIconName({ method: "direct", state: "sent" })).toBe("check");
+ expect(wrapper.vm.outboundBubbleStatusIconName({ method: "direct", state: "delivered" })).toBe("check-all");
+ expect(wrapper.vm.outboundBubbleStatusIconName({ method: "opportunistic", state: "sent" })).toBe("check");
+ expect(wrapper.vm.outboundBubbleStatusIconName({ method: "opportunistic", state: "delivered" })).toBe(
+ "check-all"
+ );
+ expect(wrapper.vm.outboundBubbleStatusIconName({ method: "propagated", state: "sent" })).toBe("email-outline");
+ expect(wrapper.vm.outboundBubbleStatusIconName({ method: "propagated", state: "delivered" })).toBe(
+ "email-check-outline"
+ );
+ expect(wrapper.vm.outboundBubbleStatusIconName({ method: "paper", state: "sent" })).toBe("note-outline");
+ expect(wrapper.vm.outboundBubbleStatusIconName({ method: "paper", state: "delivered" })).toBe(
+ "note-check-outline"
+ );
+ expect(wrapper.vm.outboundBubbleStatusIconName({ method: "direct", state: "unknown" })).toBe("check");
+ expect(wrapper.vm.outboundBubbleStatusIconName(null)).toBe("check");
+ });
+
it("outboundTransferProgressPercent and label track resource transfer", () => {
const wrapper = mountViewer();
expect(wrapper.vm.outboundTransferProgressPercent({ state: "sending", progress: 42.5 })).toBe(43);


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────